Skip to content

Add Spanish localization + in-app language picker#189

Draft
gmarull wants to merge 4 commits into
coredevices:masterfrom
teslabs:gerard/i18n-spanish
Draft

Add Spanish localization + in-app language picker#189
gmarull wants to merge 4 commits into
coredevices:masterfrom
teslabs:gerard/i18n-spanish

Conversation

@gmarull
Copy link
Copy Markdown
Member

@gmarull gmarull commented May 15, 2026

Summary

  • iOS plumbing (5967cbea) — declare en and es in CFBundleLocalizations so iOS surfaces both via NSBundle.preferredLocalizations; Compose Resources needs this to pick values-es/ at runtime on iOS.
  • Formalize composeApp strings (61679361) — migrate ~37 inline Text(\"…\") / SectionText(\"…\") literals in BugReport*, Onboarding*, RingOnboarding, WatchOnboarding screens to stringResource(), and move the Android foreground-service notification text into androidMain res. English only at this point.
  • Spanish translations (cecae7ba) — add values-es/strings.xml in every module that ships a values/strings.xml (pebble, util, experimental, composeApp commonMain, composeApp androidMain). ~130 strings total. Product names (Pebble, Index, PebbleOS) intentionally left untranslated.
  • In-app language picker (d61777aa) — new "App Language" dropdown in Settings → General right under "App Theme". Options: System (default), English, Spanish. Backed by a LocaleProvider mirroring ThemeProvider. Live switch via LocalComposeEnvironment override — no app restart.

Caveats for review

  • The runtime locale switch uses @InternalResourceApi (LocalComposeEnvironment, ComposeEnvironment, LanguageQualifier). It's the only mechanism Compose Multiplatform 1.10.x exposes for in-app locale switching; revisit when a stable API ships.
  • Dropdown title "App Language" is a hard-coded English literal to match the existing "App Theme" convention — the items list is built inside a non-Composable remember { … }, so stringResource() can't be used there without a wider refactor of basicSettingsDropdownItem.
  • Strings not extracted in this PR (still English-only literals): AppNavHost titles, setStatus(\"…\") messages, BugReportProcessor error strings, accessibility contentDescriptions, PebbleElevatedButton(text = \"…\") call-sites. These need different patterns (string passed off-Composable, or through state) and felt out of scope for an initial pass.
  • Translations are neutral / non-regional Spanish. If you want es-ES or es-419 specifically we can split.

Test plan

  • Build Android debug — verify composeApp/src/commonMain/composeResources/values-es/strings.xml and module-level values-es/ are picked up by AGP/Compose Resources.
  • Build iOS — verify CFBundleLocalizations change in iosApp/iosApp/Info.plist doesn't trip up the Xcode project's knownRegions.
  • Launch with device locale = Spanish; confirm: nav bar / settings labels, day-of-week labels (experimental module), Bug Report screen, Onboarding screens are all in Spanish.
  • Open Settings → General → "App Language". Switch between System, English, Spanish; confirm UI updates live without restart.
  • Confirm Android foreground-service notification (when watch is connected) localizes via getString(R.string.foreground_notification_text).

🤖 Generated with Claude Code

gmarull and others added 4 commits May 15, 2026 15:25
Add `en` and `es` to CFBundleLocalizations so iOS surfaces both
locales via NSBundle.preferredLocalizations. Compose Resources
relies on this to select non-base locale folders at runtime on
iOS — without it, values-es/ assets are never picked.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Migrate ~37 inline Text("…") / SectionText("…") literals across
BugReport*, Onboarding*, RingOnboarding, WatchOnboarding screens
to Compose Resources stringResource(), and populate
composeApp/src/commonMain/composeResources/values/strings.xml
with the matching English entries.

Also moves the Android foreground-service notification text
("Keeping Pebble connection alive") into androidMain res so it
flows through getString(R.string.foreground_notification_text)
and can be localised.

In BugReportScreen.kt and ViewBugReportScreen.kt, util-module
Res is aliased to UtilRes because composeApp now owns the
unaliased Res import (more call sites).

Not covered: AppNavHost titles, setStatus() messages,
contentDescription strings, BugReportProcessor error text — all
still English literals.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add values-es/strings.xml in every module that ships a
values/strings.xml: pebble, util, experimental (Compose
Resources) plus composeApp commonMain and the composeApp
Android manifest resources. Covers ~130 strings total — nav
labels, day/month names, bug-report and onboarding screens,
foreground-service notification, etc.

Product names ("Pebble", "Index", "PebbleOS") are intentionally
not translated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the ThemeProvider pattern with a new LocaleProvider:
- AppLocale enum: System (default), English, Spanish
- Persisted via Russhwolf Settings (key "app_locale")
- New dropdown in Settings > General right under "App Theme"

LocalizedApp wraps the root composition and overrides Compose
Resources' LocalComposeEnvironment when the user picks a
specific language. When AppLocale.System is selected the
override is bypassed and resource resolution falls through to
the platform default (LocalConfiguration on Android, NSBundle
preferred localizations on iOS).

The override uses @InternalResourceApi (LocalComposeEnvironment,
ComposeEnvironment, LanguageQualifier). That's currently the
only mechanism Compose Multiplatform exposes for runtime locale
switching; revisit when a stable API ships.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gmarull gmarull requested a review from sjp4 May 15, 2026 14:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant